<?php

require_once('Adept/Binding/IValue.php');

class Adept_Binding_Value implements Adept_Binding_IValue
{

    protected $objectId;
    protected $objectIndex = null;
    protected $property = null;
    protected $propertyIndex = null;

    public function __construct($string = null)
    {
        if ($string != null) {
            Adept_Binding_Parser::parseValueBinding($string, $this);
        }
    }

    protected function findObject()
    {
        $context = Adept_Context::getInstance();
        $object = $context->getAttribute($this->objectId);
        if (is_null($object)) {
            throw new Adept_Exception("Cannot find object with id: {$this->objectId} ");
        }

        if (!is_null($this->objectIndex)) {
            if (!isset($object[$this->objectIndex])) {
                throw new Adept_Exception("Cannot find index {$this->objectIndex} of id: {$this->objectId}");
            }
            $object = $object[$this->objectIndex];
        }
        return $object;
    }

    public function getValue()
    {
        $object = $this->findObject();

        if ($this->property === null) {
            return $object;
        }

        $getter = 'get' . ucfirst($this->property);
        if (!method_exists($object, $getter)) {
            $getter = 'is' . ucfirst($this->property);
            if (!method_exists($object, $getter)) {

                $property = $this->property;
                if (property_exists($object, $this->property)) {
                    if (!is_null($this->propertyIndex)) {
                        return $object->$property[$this->propertyIndex];
                    } else {
                        return $object->$property;
                    }
                }
                throw new Adept_Exception("Cannot read binded property", 0,
                    array('objectId' => $this->objectId,
                        'objectIndex' => $this->objectIndex,
                        'property' => $this->property,
                        'propertyIndex' => $this->propertyIndex));
            }
        }

        if (!is_null($this->propertyIndex)) {
            return $object->$getter($this->propertyIndex);
        } else {
            return $object->$getter();
        }
    }

    protected function setContextProperty($value)
    {
        $context = Adept_Context::getInstance();
        if ($this->objectIndex) {
            $array = $context->getAttribute($this->objectId);
            $array[$this->objectIndex] = $value;
            $context->setAttribute($this->objectId, $array);
        } else {
            $context->setAttribute($this->objectId, $value);
        }
    }

    public function setValue($value)
    {
        $object = $this->findObject();

        if ($this->property === null) {
            $this->setContextProperty($value);
            return ;
        }

        $setter = 'set' . ucfirst($this->property);

        if (!method_exists($object, $setter)) {

            $property = $this->property;
            if (property_exists($object, $property)) {
                if (!is_null($this->propertyIndex)) {
                    $object->$property[$this->propertyIndex] = $value;
                } else {
                    $object->$property = $value;
                }
                return;
            }
            throw new Adept_Exception("Cannot write binded property", 0,
            array('objectId' => $this->objectId,
            'objectIndex' => $this->objectIndex,
            'property' => $this->property,
            'propertyIndex' => $this->propertyIndex));
        }
        if (!is_null($this->propertyIndex)) {
            $object->$setter($this->propertyIndex, $value);
        } else {
            $object->$setter($value);
        }
    }

    public function getObjectId()
    {
        return $this->objectId;
    }

    public function setObjectId($objectId)
    {
        $this->objectId = $objectId;
    }

    public function getObjectIndex()
    {
        return $this->objectIndex;
    }

    public function setObjectIndex($objectIndex)
    {
        $this->objectIndex = $objectIndex;
    }

    public function getProperty()
    {
        return $this->property;
    }

    public function setProperty($property)
    {
        $this->property = $property;
    }

    public function getPropertyIndex()
    {
        return $this->propertyIndex;
    }

    public function setPropertyIndex($propertyIndex)
    {
        $this->propertyIndex = $propertyIndex;
    }

}

